15 错误检测与处理(Error Detection and Handling)
上一章
本章描述了物理层链路训练与状态机(LTSSM)的操作过程。从上电或复位开始,直至链路进入完全运行的 L0 状态(此时可进行正常数据包传输),详细阐述了链路的初始化流程。此外,还讨论了链路电源管理状态 L0s、L1、L2 和 L3 及其状态转换。同时描述了用于重新建立位锁定、符号锁定或块锁定的恢复状态,以及用于链路带宽管理的链路速率和宽度变更机制。
本章
尽管始终致力于最小化错误,但错误无法完全消除,因此错误的检测与报告至关重要。本章讨论了 PCIe 端口或链路中出现的错误类型、检测方式、报告机制及处理方案。由于 PCIe 设计需向后兼容 PCI 错误报告机制,本章首先回顾了 PCI 的错误处理方法作为背景知识,随后重点阐述 PCIe 中可纠正错误、非致命错误和致命错误的处理方式。
下一章
下一章将提供系统电源管理讨论的整体背景,并详细描述与 PCI 总线电源管理接口规范及高级配置与电源接口(ACPI)兼容的 PCIe 电源管理。PCIe 定义了针对 PCI-PM 规范的扩展,主要侧重于链路电源和事件管理。
15.1 背景(Background)
与 PCI 的软件向后兼容性是 PCIe 的重要特性,这一特性通过保留原有的 PCI 配置寄存器来实现。PCI 在每个总线传输阶段验证正确的奇偶校验以检查错误。检测到的错误记录在状态寄存器中,并可通过以下两种边带信号之一选择性地报告:PERR#(奇偶校验错误)用于数据传输期间可能可恢复的奇偶校验故障,以及 SERR#(系统错误)用于通常不可恢复的更严重问题。这两种类型可分类如下:
- 普通数据奇偶校验错误 —— 通过 PERR# 报告
- 多事务(特殊周期)期间的数据奇偶校验错误 —— 通过 SERR# 报告
- 地址和命令奇偶校验错误 —— 通过 SERR# 报告
- 其他类型的错误(设备特定) —— 通过 SERR# 报告
错误的处理方式不在 PCI 规范范围内,可能涉及硬件支持或设备特定软件。例如,从内存读取时发生的数据奇偶校验错误,可通过检测该状况并直接重复请求在硬件中恢复。若失败操作未改变内存内容,这将是安全的处理步骤。
如图 15-1(第 649 页)所示,两个错误引脚通常连接到芯片组,用于向消费级 PC 的 CPU 发送信号。这类设备对成本极为敏感,因此通常没有足够的预算来配置完善的错误处理机制。最终,所选用的错误报告信号是芯片组向处理器发送的 NMI(不可屏蔽中断)信号,用于指示需要立即处理的重大系统故障。大多数消费级 PC 并未为此情况配备错误处理程序,因此系统会直接停止运行以避免数据损坏,并通过 BSOD(蓝屏死机)通知操作员。SERR# 信号的典型触发场景是事务命令阶段检测到的地址奇偶校验不匹配——这属于潜在破坏性情况,因为错误的目标设备可能响应请求。若此类事件发生并通过 SERR# 上报,恢复过程将极为困难,通常需要大量软件开销。(关于 PCI 错误处理的更多信息,请参阅 MindShare 出版的《PCI 系统架构》一书。)
PCI-X 使用相同的两个错误报告信号,但根据是否存在设备特定的错误处理软件,定义了具体的错误处理要求。如果不存在此类处理程序,则所有奇偶校验错误均通过 SERR# 报告。
图 15-1:PCI 错误处理
PCI-X 2.0 采用源同步时钟技术以实现更高的数据传输速率(最高可达 4 GB/s)。该总线主要面向高端企业级系统,因为其成本通常超出消费级机器的承受范围。由于这些高性能系统同样需要高可用性,规范制定者选择通过增加纠错码(ECC)支持来改进错误处理能力。ECC 能够实现更强大的错误检测,并可实时纠正单比特错误,在最大限度减少传输错误影响方面具有显著优势。(欲了解更多关于 PCI-X 错误处理的信息,请参阅 MindShare 出版的《PCI-X 系统架构》一书。)
PCIe 通过使用传统配置寄存器中的错误状态位来记录与 PCI 类似的错误事件,从而保持与这些传统机制的向后兼容性。这使得传统软件能够以它理解的方式查看 PCIe 错误事件,并允许其与 PCIe 硬件协同工作。有关这些寄存器的详细信息,请参见第 674 页的“PCI 兼容错误报告机制”。
15.2 PCIe 错误定义(PCIe Error Definitions)
规范中使用了四个关于错误的通用术语,定义如下:
- 错误检测 - 确定错误存在的过程。错误由代理因本地问题(例如接收到损坏的数据包)或因为接收到来自其他设备(如投毒数据包)的错误信号数据包而发现。
- 错误记录 - 根据检测到的错误,在架构寄存器中设置相应的位,以辅助错误处理软件。
- 错误报告 - 通知系统存在错误状况。这可以采取向根复合体传递错误消息的形式(假设设备已启用发送错误消息)。当根复合体接收到错误消息时,可以反过来向系统发送中断。
- 错误信号传递 - 一个代理通过发送错误消息、发送带有 UR(不支持请求)或 CA(完成者中止)状态的完成报文、或投毒 TLP(也称为错误转发)来通知另一个代理存在错误状况的过程。
15.3 PCIe 错误报告(PCIe Error Reporting)
PCIe 定义了两个错误报告级别。第一个是所有设备必须支持的基线能力(Baseline capability),包括对传统错误报告的支持以及 PCIe 错误报告的基本支持。第二个是可选的高级错误报告能力(Advanced Error Reporting Capability),它新增了一组配置寄存器,能够追踪更多关于已发生错误的详细信息,包括错误的严重程度,在某些情况下甚至能记录导致错误的数据包信息。
15.3.1 基线错误报告(Baseline Error Reporting)
所有设备都需要两组配置寄存器来支持基线错误报告。这些寄存器在第 674 页的“基础错误检测与处理”中有详细描述,此处仅作简要总结:
PCI 兼容寄存器——这些寄存器与 PCI 使用的寄存器相同,为现有的 PCI 兼容软件提供向后兼容性。为实现这一功能,PCIe 错误会被映射为 PCI 兼容错误,从而使传统软件能够识别这些错误。
PCI Express 能力寄存器——这些寄存器仅对支持 PCIe 的新版软件有用,但它们能为 PCIe 软件提供更详细的错误信息。
15.3.2 高级错误报告(AER)(Advanced Error Reporting)
这一可选的错误报告机制包含一套全新且专用的配置寄存器,可为错误处理软件提供更多信息,用于诊断和恢复问题。AER 寄存器映射到扩展配置空间中,能够提供关于任何错误性质的更详细信息。有关这些寄存器的详细说明,请参见第 685 页的“高级错误报告(AER)”。
15.4 错误类别(Error Classes)
根据硬件是否能够修复问题,错误分为两大类:可纠正错误和不可纠正错误。不可纠正错误又根据软件能否修复问题进一步细分为非致命错误和致命错误。
- 可纠正错误 —— 由硬件自动处理。
- 不可纠正错误:
- 非致命错误 —— 由设备特定软件处理;链路仍可运行,可能实现无数据丢失的恢复。
- 致命错误 —— 由系统软件处理;链路或设备运行异常,无数据丢失的恢复可能性较低。
基于这些分类,错误处理软件可划分为独立的处理程序来执行所需操作。此类操作范围广泛,从简单监控可纠正错误的频率,到在发生致命错误时重置整个系统。无论错误类型如何,软件均可安排系统接收所有错误通知,以便进行追踪和记录。
15.4.1 可纠正错误(Correctable Errors)
可纠正错误,顾名思义,会在硬件层面自动修复。它们可能因增加延迟和消耗带宽而影响性能,但只要一切顺利,恢复过程是自动且快速的——这既无需软件干预,也不会造成信息丢失。这类错误 无需向软件报告,但若进行报告,软件便可追踪错误趋势,从而发现某些设备可能即将出现故障的迹象。
15.4.2 不可纠正错误(Uncorrectable Errors)
无法在硬件层面自动修复的错误被称为不可纠正错误,其严重程度分为非致命性和致命性两类。
15.4.2.1 非致命不可纠正错误(Non-fatal Uncorrectable Errors)
非致命错误表示信息已丢失,但原因可能并非链路或设备的完整性故障。数据包在传输过程中某处失败,但链路仍能正常运行,其他数据包不受影响。由于链路仍在工作,丢失的信息或许可以恢复,但这取决于具体实现中的软件处理方式。此类错误的一个示例是完成超时——当请求已发送,但在规定时间内未收到完成响应时发生。虽然某处出现了问题,但可能只是交换机中的随机比特错误导致完成响应被错误路由。针对这种情况的恢复尝试可能只需重新发送请求即可。
15.4.2.2 致命不可纠正错误(Fatal Uncorrectable Errors)
致命错误表示链路或设备发生操作故障,导致数据丢失且难以恢复。针对此类情况,重置至少一个故障链路或设备很可能是任何恢复流程的首要步骤,因为该组件显然因某种原因无法正常运行。规范也允许采用特定实现方案,允许软件尝试限制故障影响,但并未定义任何必须采取的具体操作。此类错误的典型示例是接收缓冲区溢出——由于流量控制跟踪计数器不同步导致信息丢失。由于缺乏修复机制,通常需要重置该链路。
15.5 PCIe 错误检测机制(PCIe Error Checking Mechanisms)
PCIe 错误检测的范围主要关注与链路及数据包传输相关的错误(如图 15-2 第 653 页所示)。与链路传输无关的错误不会通过 PCIe 错误处理机制上报,需要通过设备特定中断等专有方法进行报告。接口的每一层都包含错误检查功能,以下各节将对此进行总结。
图 15-2:PCI Express 错误检查与报告范围
15.5.1 CRC
在深入探讨各层相关的错误处理之前,首先讨论 CRC(循环冗余校验)的概念会有所帮助,因为它是 PCIe 错误检查的重要组成部分。CRC 码由发送方根据数据包内容计算得出,并附加到数据包中进行传输。CRC 的名称源于以下事实:这种校验码(根据数据包计算以检查错误)是冗余的(不向数据包添加任何信息),并且源自循环码。尽管 CRC 无法像 ECC(纠错码)那样提供足够的信息来自动纠正错误,但它确实提供了强大的错误检测能力。CRC 也常用于串行传输,因为它们擅长检测一连串的错误比特。
CRC 在 PCIe 中有两种不同的使用场景。一种是强制性的 LCRC(链路 CRC),在数据链路层为每条经过链路的 TLP 生成并校验,用于检测链路上的传输错误。
第二个是可选的 ECRC(端到端循环冗余校验),它在发送端的事务层生成,并在数据包最终目标的事务层进行校验。该机制旨在检测可能被忽略的错误,例如当 TLP 通过交换机等中间代理时(如图 15-3 第 654 页所示)。在此示意图中,数据包虽安全抵达交换机的下行端口,但在交换机内部存储或处理过程中发生了位错误。LCRC 仅保护链路上的 TLP。当入口端口的数据链路层校验 LCRC 后,会将其从数据包中移除,因为出口端口将重新计算包含新序列号的 LCRC。这意味着数据包在交换机内部处于无保护状态——这正是 ECRC 存在的意义。ECRC 在源端设备计算生成,中间设备不会移除或重新计算。因此,若目标设备校验 ECRC 时发现不匹配,即使未检测到 LCRC 错误,也说明传输路径中必然存在错误。 请注意,使用 ECRC 需要存在可选的增强错误报告寄存器,因为它们包含启用此功能的位。
图 15-3:ECRC 使用示例
15.5.2 各层的错误检查(Error Checks by Layer)
接收端的不同层会对传入数据包的不同方面进行检查。部分错误检查被列为可选。对于这些情况,如果发生错误但设计者选择不实现该检查方式,则错误将无法被检测到。
15.5.2.1 物理层错误(Physical Layer Errors)
到达接收端的数据包首先进入物理层。在该层级,有些检查是必须执行的,而其他检查则可根据需要选择执行。链路训练也在该层进行,过程中可能出现多种问题,但物理层的这些细节及其他相关内容将在第 505 页第 14 章《链路初始化与训练》中详细说明。简而言之,物理层错误(也称为接收端错误或链路错误)包括以下情况:
- 使用 8b/10b 编码时,需检查解码违规(强制检查)
- 帧违规(8b/10b 编码下可选,128b/130b 编码下必需)
- 弹性缓冲区错误(可选检查)
- 符号锁定丢失或通道去偏斜(可选检查)
如果在检测到接收器错误时正在进行 TLP,则该 TLP 将被丢弃。为解决该错误,若尚未有挂起的 NAK,则通知数据链路层发送一个 NAK。
15.5.2.2 数据链路层错误(Data Link Layer Errors)
经过物理层处理后,传入的数据包将进入数据链路层,在此处会检查是否存在多种潜在问题。这些错误条件的详细说明可参见第 317 页第 10 章“确认/否认协议”。概括而言,错误类型包括:
- TLP 的 LCRC 校验失败
- TLP 的序列号违规
- DLLP 的 16 位 CRC 校验失败
- 链路层协议错误
与物理层类似,如果在传输 TLP 过程中检测到错误,该 TLP 将被丢弃,并且如果尚未有 NAK 待处理,则会安排发送 NAK。
在发送端也需要关注一些数据链路层错误,包括 REPLAY_TIMER 超时和 REPLAY_NUM 计数器溢出。超时处理方式为重放重放缓冲区中的内容,并递增 REPLAY_NUM 计数器。每当发送端接收到表明已取得进展(即清除重放缓冲区中的一个或多个 TLP)的 ACK 或 NAK 时,计时器和计数器会被重置。但如果未能及时收到 Ack 或 Nak,则会触发超时条件,从而导致重放。
15.5.2.3 事务层错误(Transaction Layer Errors)
最后,如果传入的 TLP 通过了物理层和数据链路层的所有检查,它们最终将到达事务层,并在该层检查以下内容:
- ECRC 失败(可选检查)
- 格式错误的 TLP(数据包格式错误)
- 流控制协议违规
- 不支持的请求
- 数据损坏(投毒数据包)
- 完成者中止(可选检查)
- 接收器溢出(可选检查)
与数据链路层类似,发送端事务层也存在一些错误检查,例如:
- 完成超时
- 意外完成(完成与待处理请求不匹配)
15.6 错误污染(Error Pollution)
如果某个设备在同一事务中检测到多个问题,就可能引发问题。这可能导致多个错误被报告(称为“错误污染”)。为避免这种情况,报告的错误仅限于最严重的一个。例如,如果某个 TLP 在物理层出现接收器错误,那么它在数据链路层和事务层也必然存在错误,但全部报告只会增加混乱。最相关的是报告最先发现的错误。因此,如果在物理层发现错误,就没有必要将数据包转发到更高层。同样,如果在数据链路层发现错误,数据包也不会被转发到事务层。某一层出现问题的数据包不会被转发到下一层,而是被丢弃。
尽管如此,同一数据包在事务层仍可能发现多个错误。按照规范定义的优先级顺序,只应报告最严重的一个。事务层错误优先级从高到低依次为:
- 不可纠正的内部错误
- 接收缓冲区溢出
- 流控制协议错误
- ECRC 检查失败
- 格式错误的 TLP
- 原子操作出口被阻止
- TLP 前缀被阻止
- ACS(访问控制服务)违规
- MC(多播)阻塞的 TLP
- UR(不支持请求)、CA(完成者中止)或意外完成
- 收到损坏的 TLP
例如,一个 TLP 可能因报头损坏而出现 ECRC 错误。由于数据包内部存在损坏,该错误也可能被视为格式错误或不受支持的请求。ECRC 错误的优先级最高,因为这意味着报头内容可能已损坏,因此报告依赖于这些内容的错误毫无意义。
15.7 PCIe 错误来源(Sources of PCI Express Errors)
与其逐一考虑所有错误情况,不如将它们归类到常见领域中,这样会更有帮助。
15.7.1 ECRC 生成与校验(ECRC Generation and Checking)
如前所述,ECRC 的生成与校验需要存在可选的“高级错误报告配置寄存器”结构,如第 658 页图 15-4 所示。配置软件通过检测该能力寄存器来判断某个功能是否支持 ECRC。若支持,则可通过写入“错误能力与控制寄存器”来启用该功能。
图 15-4:错误相关配置寄存器位置

支持生成 ECRC 的设备在发起 TLP(请求或完成报文)时,会根据报文头部和数据部分计算 32 位 ECRC,并将其附加到报文末尾。ECRC 被称为“端到端”校验码,其设计初衷是在 TLP 的源端生成,且沿途任何中间设备均不得剥离或重新生成。位于源端与目标端之间的交换机可以检查并报告 ECRC 错误,但并非强制要求。无论是否存在错误,交换机必须保持报文原样转发,以便最终目标设备能够评估 ECRC 并采取相应措施。若交换机作为 TLP 的发起方或接收方,则可像普通设备一样参与 ECRC 的生成与校验。关于交换机如何报告此类错误的更多信息,请参见第 670 页“建议性非致命错误”章节。
15.7.1.1 TLP 摘要(TLP Digest)
如果启用了可选的 ECRC 功能,则会在报头中设置一个称为 TD(TLP 摘要)的特殊位,以指示其存在于数据包的末尾(ECRC 也称为摘要)。数据包报头中的 TD 位如图 15-5(第 659 页)所示。规范强调,在转发 TLP 时必须特别小心处理该位,因为如果该位缺失但 ECRC 存在,或者反之,则该数据包将被视为格式错误。
图 15-5:完成报头中的 TD(TLP Digest)位
TD 位位于 TLP 报头的第一个双字中,用于指示该 TLP 末尾是否携带 ECRC(Digest)。若 TD 位与实际是否存在 ECRC 不一致,该 TLP 将被视为格式错误的 TLP。
15.7.1.2 ECRC 机制中不包含的变体位(Variant Bits Not Included in ECRC Mechanism)
ECRC 是根据头部和数据的内容计算得出的。由于这些内容预期不会发生变化,因此在接收端执行校验时结果应该相同。然而,实际上有两个头部位在数据包传输过程中可以合法改变:类型字段的第 0 位和 EP 位。类型字段的第 0 位在配置请求中可能发生变化,原因很简单:请求在到达目标总线之前是类型 1,到达后会变为类型 0。这涉及改变类型字段的第 0 位。EP 位也可能被中间设备在检测到数据错误时合法更改。例如,如果交换机转发 TLP 时发生某种内部错误导致数据损坏,在数据包从出口端口发出时设置 EP 位是报告错误的一种方式(称为错误转发或数据投毒)。
由于这两个位在数据包传输过程中可能发生变化,因此被称为“可变位”,不能用于生成或校验 ECRC。相反,在生成和校验 ECRC 时,它们的值始终假定为 1b,而非使用实际值。这样 ECRC 就不会依赖于它们,从而能被正确评估。
检测到 ECRC 错误时采取的操作超出了本规范的范围,但可能的选择取决于该错误是在请求中还是在完成中发现的。
- 请求中的 ECRC——检测到 ECRC 错误的完成者必须设置 ECRC 错误状态位。它们也可以选择不为此请求返回完成,从而导致请求者处发生完成超时,其软件随后可能选择重新调度该请求。
- 完成中的 ECRC——检测到 ECRC 错误的请求者必须设置 ECRC 错误状态位。除了标准错误报告机制外,它们还可以选择通过特定功能的中断向设备驱动程序报告该错误。如前所述,软件可能决定重新调度失败的请求。
无论哪种情况,系统都可能收到一条不可纠正的非致命错误消息。若如此,设备驱动程序可能会被访问以检查不可纠正错误状态寄存器中的状态位,从而了解错误的性质。如果可能,失败的请求可能会被重新调度,但可能还需要其他步骤。
15.7.2 数据投毒(Data Poisoning)
数据投毒(也称为错误转发)为设备提供了一种可选方式,用于指示与 TLP 关联的数据已损坏。在这些情况下,数据包头部中的 EP(错误投毒)位会被置位以指示错误。EP 位如图 15-6(第 660 页)所示。
图 15-6:完成报头中的 EP(Error Poisoned)位
每当数据传输时(例如在写入请求或带数据的完成操作中),数据损坏可能发生,且需要向目标设备报告。在这些情况下,数据包可以转发给接收方,但通过报头中的 EP 位标记为包含错误数据。细心的读者可能会疑惑,为何要发送已知有误的数据。实际上,在某些场景下这样做是有用的:
- 如果某个请求返回了带数据的完成操作,但在从目标设备收集数据时遇到错误(例如内存中的奇偶校验或 ECC 故障),那么最佳报告方式是什么?一种方法是完全不发送该完成操作,但如果错误未通过其他方式报告,系统在请求方只会看到完成超时。这种响应作用有限,因为多种问题都可能导致该结果。 另一方面,如果完成报文在传输时设置了中毒位,那么至少请求者可以确认到完成者的往返路径是正常工作的。因此,问题必然发生在完成者内部或路径上的某个交换机中。具体采取何种措施取决于具体实现,但与完成报文仅超时的情况相比,此时对故障原因的了解更为明确。
- 该机制可用于报告中间环节出现的问题。当数据载荷在通过交换机时发生损坏,数据包仍可继续转发,但会设置错误投毒位(EP 位)以标识该问题。
- 目标设备可能能够接受存在错误的数据。例如,音频输出设备需要及时接收数据流才能正常工作。若输入数据存在错误,其影响较小(仅导致音频输出出现短暂杂音),而恢复数据所需的时间足以造成明显延迟,因此直接接受带错误的数据可能比尝试恢复数据更为妥当。
- 目标设备可能具备数据纠正手段。数据可能可直接恢复,或目标设备可能具备重新生成部分数据的能力,亦或拥有其他绕过问题的方法。
规范指出,数据投毒仅适用于与数据包关联的数据载荷(例如内存、配置或 I/O 写入及完成操作),绝不涉及 TLP 报头内容。因此,若接收器检测到无载荷的投毒数据包 (如中毒的内存读取操作),其行为将处于未定义状态。投毒操作仅能在设备的事务层执行;数据链路层不会检查或影响 TLP 报头内容。
错误转发支持被声明为发送方的可选功能,而接收方未获得此类声明,这意味着该功能对接收方而言并非可选。
如果发送器支持该功能,则通过传统命令寄存器中的奇偶错误响应位启用该功能。这是因为投毒数据包大致相当于 PCI 中的奇偶错误,因为 PCI 正是通过这种方式报告错误数据的。如果启用,接收投毒数据包可能会通过错误消息报告给系统;如果存在可选的增强错误报告寄存器,还会设置投毒 TLP 状态位。
正如预期的那样,对控制位置的投毒写入不允许修改目标中的内容。规范中给出的示例包括配置写入、对控制寄存器的 I/O 或内存写入以及原子操作。接收投毒数据包的交换机必须将其原封不动地转发到目标端口,但如果已启用相应功能,则必须将此数据包作为错误报告,以帮助软件确定错误发生的位置。接收投毒的非发布请求的完成者应返回状态为 UR(不支持请求)的完成。
15.7.3 分裂事务错误(Split Transaction Errors)
在与非发布事务(Non-Posted Transaction)相关的分裂事务过程中,可能发生多种故障。PCIe 在完成报头中定义了一个状态字段,允许完成者向请求者报告某些错误。第 662 页的图 15-7 展示了该字段在完成报头中的位置,第 663 页的表 15-1 给出了可能的值。如表所示,仅定义了四种编码,其中两种表示错误条件。
图 15-7:完成报头中的完成状态字段
表 15-1:完成代码及描述
| 状态码 | 完成状态定义 |
|---|---|
| 000b | 成功完成(SC) |
| 001b | 不支持的请求(UR)- 错误 |
| 010b | 配置请求重试状态(CRS) |
| 011b | 完成者中止(CA)- 错误 |
| 100b - 111b | 保留 |
15.7.3.1 不支持的请求(UR)状态(Unsupported Request (UR) Status)
如果接收方不支持某个请求,它会返回一个状态为 UR 的完成报文。规范定义了若干可能导致 UR 状态的条件,例如:
- 不支持的请求类型(例如,对原生端点的 I/O 请求,或对原生端点的 MRdLk 请求)
- 包含不支持或未定义消息代码的消息
- 请求未引用映射到设备的地址空间
- 请求地址未映射到交换端口的地址范围内
- 投毒写入请求@#0 针对完成者中的 I/O 或内存映射控制空间。此类请求不得允许修改该位置,而应由完成者丢弃,并通过返回带有 UR 状态的完成报文进行报告。
- 下游根端口或交换端口接收到针对其次级总线上不存在的设备(例如设备编号非零的设备,除非启用了 ARI)的配置请求。该端口必须终止该请求并返回带有 UR 状态的完成报文,因为下游设备编号必须为零(除非启用了 ARI,即替代路由 ID 解释)。
- 在端点处接收到类型 1 配置请求。
- 使用保留的完成状态字段编码的完成必须被解释为 UR。
- 处于 D1、D2 或 D3 电源管理状态的函数接收到配置请求或消息以外的请求。
- 一个在其头部未设置 No Snoop 位的 TLP 被路由到其 VC 资源能力寄存器中设置了 Reject Snoop Transactions 位的端口。
15.7.3.2 完成者中止 (CA) 状态(Completer Abort (CA) Status)
以下几种情况可能导致完成者向请求者返回此 CA 状态。例如:
- 完成者收到一个请求,但若完成该请求将违反其编程规则。例如,某些功能可能被设计为仅允许以完整且对齐的方式访问某些寄存器(如一个 4 字节寄存器可能要求 4 字节对齐访问)。任何以部分或未对齐方式访问这些寄存器的尝试(例如仅读取 4 字节寄存器中的两个字节)都将失败。此类限制并非违反规范,而是与该功能编程接口相关的合法约束。对此类功能的访问基于设备驱动程序应理解如何访问其功能的预期。
- 完成者收到一个请求,但由于设备中存在某种永久性错误状态而无法处理。例如,无线局域网卡在未连接经批准的天线前,无法通过无线电收发数据包,因此拒绝接受新数据包。
- 完成者接收到一个请求,并检测到 ACS(访问控制服务)错误。例如,一个实现了 ACS 寄存器并启用了 ACS 翻译阻止功能的根端口,如果在该端口上看到 AT 字段的值不是默认值的内存请求,则构成 ACS 违规。
- PCIe 转 PCI 桥可能接收到一个针对 PCI 总线的请求。如果目标设备因永久性条件或违反功能编程规则而无法完成该请求,PCI 允许目标设备发出目标终止信号。作为响应,桥接器将返回一个状态为 CA 的完成报文。
中止请求的完成者可能会通过非致命错误消息向根节点报告该错误,并且如果该请求需要完成报文,则状态将为 CA。
15.7.3.3 意外完成(Unexpected Completion)
当请求者收到完成报文时,它会使用事务描述符(请求者 ID 和标签)将其与之前的请求进行匹配。在极少数情况下,事务描述符可能与任何先前的请求都不匹配。这种情况可能发生是因为完成报文在返回给预期请求者的途中被错误路由。接收到意外完成报文的设备可以发送一个建议性非致命错误消息,但预期正确的请求者最终会超时并采取相应措施,因此该错误消息的优先级较低。
15.7.3.4 完成超时(Completion Timeout)
对于挂起请求从未收到其预期的完成报文的情况,规范定义了一个完成超时机制。规范明确旨在通过该机制检测完成报文没有合理机会返回的情况;该超时时间应长于任何正常预期的延迟。
所有发起期望完成(Completion)请求的设备都必须实现完成超时定时器,但仅发起配置事务的设备除外。另请注意,每个等待完成的请求都是独立计时的,因此必须有一种方法跟踪每个未完成事务的时间。规范 1.x 和 2.0 版本对超时值的允许范围定义如下:
- 强烈建议设备在发送请求后不早于 10 ms 触发超时;但如果设备需要更高的粒度,超时最早可发生在 50 μs。
- 设备必须不晚于 50 ms 触发超时。
从 2.1 规范修订版开始,PCI Express 能力块中新增了设备控制寄存器 2(Device Control Register 2),使软件能够查看和控制超时值,如图 15-8(第 665 页)所示。
图 15-8:设备控制寄存器 2
如果请求需要多个完成(Completion)才能返回所请求的数据,则单个完成不会停止定时器。相反,定时器会持续运行,直到所有数据返回完毕,无论需要多少个完成。如果在超时发生时仅返回了部分数据,请求者可以丢弃或保留该数据。
15.7.4 链路流控制相关错误(Link Flow Control Related Errors)
在将数据包转发到数据链路层进行传输之前,事务层必须检查流控制(FC)信用,以确保链路对端的接收缓冲区有足够的空间来容纳该数据包。流控制违规可能发生,且被视为不可纠正的错误。与流控制相关的协议违规可通过接收流控制信息的端口检测到并与之关联。以下是一些示例:
- 链路伙伴在 FC 初始化期间,未能为任何虚拟通道通告至少规范定义的最小 FC 信用数。
- 链路伙伴通告的 FC 信用数超过允许的最大值(数据载荷最多 2047 个未使用信用,报头最多 127 个未使用信用)。
- 接收到包含非零信用值的 FC 更新,而这些信用字段最初被通告为无限。
- 接收缓冲区溢出,导致数据丢失。此检查为可选,但检测到的违规将被视为致命错误。
15.7.5 格式错误的 TLP(Malformed TLP)
到达事务层的 TLP 会检查是否存在数据包格式规则违规。数据包格式违规被视为致命错误,因为这意味着发送方在协议方面出现了严重失误,例如未能正确维护其计数器,导致其行为不再符合预期。以下是一些被视为格式错误(畸形)的数据包示例:
数据有效载荷超过最大有效载荷大小。
数据长度与头部指定的长度不匹配。
内存起始地址与长度组合导致事务跨越自然对齐的 4KB 边界。
TLP 摘要(TD 字段)指示与数据包大小不匹配(ECRC 意外缺失或存在)。
字节使能违规。
未定义的类型字段值。
违反读取完成边界(RCB)值的完成操作。
针对非配置请求的请求,返回状态为配置请求重试状态的完成操作。
流量类别字段包含未分配给已启用虚拟通道的值(这也称为 TC 过滤)。
I/O 和配置请求违规(可选检查)——例如,TC 字段、Attr[1:0] 字段以及 AT 字段必须全部为 0,而 Length 字段的值必须为 1。
向下游发送的中断仿真消息(可选检查)。
接收到的 TLP 存在 TLP 前缀错误:
- 存在 TLP 前缀但没有 TLP 报头。
- 端到端 TLP 前缀位于本地前缀之前。
- 不支持的本地 TLP 前缀类型。
- 超过 4 个端到端 TLP 前缀。
- 端到端 TLP 前缀数量超过支持范围。
需要使用 TC0 的事务类型具有不同的 TC 值:
- I/O 读取或写入请求及相应的完成操作。
- 配置读取或写入请求及相应的完成操作。
- 错误消息。
- INTx 消息。
- 电源管理消息。
- 解锁消息。
- 插槽电源消息。
- LTR 消息。
- OBFF 消息。
AtomicOp 操作数不符合架构值。
AtomicOp 地址未与操作数大小自然对齐。
针对事务类型的路由不正确(例如,需要路由至根复合体的事务被检测到正在远离根复合体)。
15.7.6 内部错误(Internal Errors)
15.7.6.1 问题所在
PCIe 规范的最初版本并未包含一种机制,用于报告设备内部发生的、与接口本身事务无关的错误。对于端点设备而言,这并非真正的问题,因为它们有与之关联的、由供应商提供的设备驱动程序,可以检测并报告内部错误。然而,交换机被视为由操作系统管理的系统资源,通常没有软件来协助检测内部错误。在高端系统中,错误隔离能力至关重要,因此交换机供应商创建了专有的内部错误处理机制。不幸的是,由于不同供应商的解决方案互不兼容,最终导致这些机制很少被使用。
15.7.6.2 解决方案
为了缓解这一问题,自 2.1 规范版本起,新增了标准化的内部错误报告选项。内部错误的定义超出了本规范的范围,但可以将其报告为“已纠正内部错误”或“不可纠正内部错误”。
“已纠正内部错误”指硬件已屏蔽或规避该错误,未造成信息丢失或异常行为。例如,内部存储位置发生的 ECC 错误被自动纠正。而“不可纠正内部错误”则意味着操作异常已导致潜在数据丢失,例如内部存储位置出现奇偶校验错误。报告内部错误为可选功能,若启用,则必须配备 AER(高级错误报告)寄存器以提供支持。
15.8 错误报告方式(How Errors are Reported)
15.8.1 引言(Introduction)
PCI Express 包含三种错误报告方法,如下所示。前两种方法——完成状态和投毒数据包——已在前面介绍过,因此接下来我们将讨论错误消息。
- 完成状态 —— 完成状态向请求者报告错误
- 投毒数据包 —— 向接收者报告 TLP 中的错误数据
- 错误消息 —— 向主机(软件)报告错误
15.8.2 错误消息(Error Messages)
PCIe 取消了 PCI 中的边带信号,并用错误消息取而代之。这些消息提供了 PERR# 和 SERR# 信号无法传达的信息,例如识别检测功能并指示错误的严重程度。图 15-9 展示了错误消息的格式。请注意,它们会被路由到根复合体进行处理。消息代码定义了所发送消息的类型。毫不意外,规范定义了三种类型的错误消息,如表 15-2 所示。
表 15-2:错误消息代码及描述
| 消息代码 | 名称 | 描述 |
|---|---|---|
| 30h | ERR_COR | 设备检测到可纠正的错误。该错误由硬件自动修正,无需软件干预。但报告这些错误仍有助于软件监控趋势,例如可纠正错误数量持续增长的情况。 |
| 31h | ERR_NONFATAL | 表示不可纠正的非致命错误。没有可用的硬件纠错机制,但链路仍能可靠工作。需要软件介入来解决问题。 |
| 33h | ERR_FATAL | 表示不可纠正的致命错误。没有可用的硬件纠错机制,且链路操作在某个重要方面已失败。需要软件介入,并且可能需要重置至少一个设备才能解决此问题。 |
图 15-9:错误消息格式
错误消息采用 Message TLP 格式,消息代码字段用于区分 ERR_COR、ERR_NONFATAL 和 ERR_FATAL,且此类消息会被路由到根复合体。 ##### 15.8.2.1 建议性非致命错误
既然我们已经看到两种类型的不可纠正错误都需要软件介入处理,那么说在某些情况下设备最好不报告其检测到的非致命错误似乎有违直觉,但确实存在这种情况。这些情况主要取决于检测代理(请求者、完成者或中间设备)的角色以及错误类型。问题在于多个设备可能报告由同一事件引发的错误,在某些平台上,发送非致命错误消息(ERR_NONFATAL)反而会妨碍软件正确处理该错误。例如,若端点设备报告错误,其设备驱动程序将被调用来处理该状况。然而,若交换器先针对同一事务报告错误,系统软件可能会被调用来进行调查,却可能无法理解驱动程序试图完成的操作或最佳响应方案。
该示例说明,某些检测代理并非判断错误最终处置的最佳选择,因此不应发送不可纠正消息。相反,此类代理可通过 ERR_COR 向软件发送建议性通知。这既避免了不可纠正错误来源的混淆,又能为软件提供更多事件信息。最终,当检测到错误时,适当的检测代理将发送 ERR_NONFATAL 消息。从 1.1 规范修订版开始,PCI Express 设备能力寄存器中新增了一个字段,用于指示对此能力的支持,如图 15-10(第 670 页)所示。所有符合 1.1 或更高版本规范的代理必须设置此位。
图 15-10:设备能力寄存器
尽管存在上述原因,但软件可能希望在中间设备检测到某些建议性错误时立即停止操作。由于较新的设备始终会执行基于角色的错误报告,因此需要一种覆盖机制。为处理这种情况,软件可以在 AER(高级错误报告)寄存器中将建议性错误的严重性从非致命提升为致命。由于不存在“建议性致命”的情况,若已启用,无论设备角色如何,该错误现在都将作为致命错误(ERR_FATAL)进行报告。
15.8.2.2 建议性非致命情况
规范列出了五种情况,在这些情况下建议性消息(ERR_COR)优先于 ERR_NONFATAL 消息。在每种情况下,检测代理都会将错误作为建议性非致命错误处理。这意味着,假设代理具有 AER 寄存器且已启用 ERR_COR,非致命条件将通过发送 ERR_COR 来处理。如果代理没有 AER 寄存器或未启用 ERR_COR,则不会发送任何错误消息。这五种情况如下:
- 完成者发送了一个带有 UR 或 CA 状态的完成报文。在这种情况下,预期请求者将具备一种机制,在发现违规完成报文时处理该错误,并且请求者是发送所需错误消息的最佳代理。来自完成者的 ERR_NONFATAL 消息只会造成混淆,因此必须将其作为建议性非致命错误(ERR_COR)处理。 奇怪的是,PCIe 并没有提供一种机制让请求者报告其收到了带有此状态的完成报文。相反,需要采用设计特定的方法(如中断)来引起设备驱动程序的注意。一个重要的例子是,当根复合体收到针对配置读取请求的带有 UR 或 CA 状态的完成报文时。在某些平台上,针对这种情况的响应是向软件返回全 1,以支持与 PCI 枚举(配置探测)软件的向后兼容性。
- 中间设备检测到错误。这种情况出现在使用交换机的系统中,因为检测代理可能不是 TLP 的最终目的地。以第 672 页图 15-11 为例,该图展示了一个通过中间交换机传递的投毒数据包。交换机将该 TLP 视为非致命错误,但只能发送 ERR_COR 消息(前提是已启用该功能)。 为了进一步探讨这一概念,为什么我们不希望交换机报告 ERR_NONFATAL?其中一个原因可以通过查看 AER 寄存器中的错误跟踪来理解。第 672 页图 15-12 显示了跟踪进入根端口的错误消息的源 ID(发送设备的 BDF)的 AER 寄存器,我们可以看到只有一个空间可用。 不可纠正的错误。如果检测到多个不可纠正的错误,将记录该事实,但仅保存第一个源 ID,因为它被认为是后续错误的可能原因。因此,不可纠正的错误必须由最合适的设备报告,这一点非常重要。值得注意的是,中间设备报告 ERR_COR 仍然有帮助,因为这允许软件确定错误最初是在哪里检测到的。
图 15-11:基于角色的错误报告示例

图 15-12:高级源 ID 寄存器
AER 能力结构中的错误源标识寄存器
ERR_FATAL/非致命 源 ID (ROS)
ERR_COR 源 ID (ROS)
ROS:只读且粘滞
再举一个例子,对于 1.0a 设备,如果清除了 UR 报告使能位,但不具备基于角色的错误报告能力,则在检测到 UR 错误时(针对发布或非发布的请求),无法报告任何错误消息。相比之下,符合 1.1 或更高版本的完成者,如果设置了 SERR# 使能位,即使清除了非支持请求报告使能位,也会针对错误的发布请求发送 ERR_NONFATAL 或 ERR_FATAL 消息,以避免静默数据损坏。但对于接收到的非发布请求,它不会发送错误消息,以支持通过配置读取进行探测的 PCI 兼容配置方法。建议软件对于不具备基于角色的错误报告能力的设备,保持 UR 错误报告使能位为清除状态,而对于具备该能力的设备则设置该位。这样,UR 错误会在错误的发布请求上报告,但不会在错误的非发布请求(如配置探测事务)上报告,从而保持与旧版软件的向后兼容性。 该规范还提到,如果根节点作为中间代理,发送给根节点的投毒 TLP 将以相同方式处理,但有一个例外:如果根节点不支持错误转发,它将无法通过 TLP 传达中毒错误,而必须将其报告为非致命错误。
- 目标设备接收到投毒 TLP。通常情况下,端点会在此情况下报告非致命错误,但此规则存在一个例外:如果最终目标设备能够以允许继续运行的方式处理中毒数据,则必须将此情况视为建议性非致命错误。
这种行为的一个示例是音频设备接收到已中毒的流数据。在这种情况下,即使数据已知已损坏,也可能被接受,因为暂停音频流足够长时间以获取软件关注并采取补救措施,比允许声音输出中出现故障更糟糕。
- 请求者遇到了完成超时。这与前一种情况类似;如果请求者有能力在问题存在的情况下继续操作,则必须将其视为建议性非致命错误。在这种情况下,请求者的一种简单解决方法是重新发送请求,并希望这次能获得更好的结果。显然,这仅在前一次请求未产生任何副作用时才有意义,但请求者可以按需多次执行此操作(尽管规范要求重试次数必须是有限的)。
- 收到意外完成。这必须作为建议性非致命错误处理。原因是这可能是由路由错误的完成消息导致的,而原始请求者最终会报告完成超时。为了让其他请求者能够重试失败的
请求,重要的是看到意外完成的请求者不要发送非致命消息。
15.9 基线错误检测与处理(Baseline Error Detection and Handling)
本节定义了检测和报告 PCI Express 错误所需的支持。合规设备必须包括:
- PCI 兼容支持 —— 必须支持 PCI 兼容的错误控制和状态字段,以兼容不了解 PCI Express 的旧版软件。
- PCI Express 错误报告 —— 使用标准 PCIe 结构进行错误控制和状态管理,可供了解 PCI Express 的新版软件使用。
15.9.1 PCI 兼容错误报告机制
15.9.1.1 通用
为了向后兼容,PCI Express 错误被映射到原有的 PCI 配置寄存器位中,使得符合 PCI 规范的软件能够访问错误状态和控制信息。从 PCI 兼容的角度理解可用特性时,需考虑配置头中命令寄存器和状态寄存器的错误相关位。部分字段定义已修改,以反映相关的 PCIe 错误条件及报告机制。PCI 兼容寄存器所追踪的 PCI Express 错误包括:
- 事务投毒/错误转发(等同于 PCI 中的数据奇偶校验错误)
- 完成者终止(CA)由完成者检测(等同于 PCI 中的目标终止)
- 不支持请求(UR)由完成者检测(等同于 PCI 中的主设备终止)
如前所述,PCI 报告错误的机制是断言 PERR#(数据奇偶校验错误)和 SERR#(不可恢复错误)。PCI Express 报告这些事件的机制是完成报文中的完成状态值和发送至根复合体的错误消息。
15.9.1.2 传统命令与状态寄存器(Legacy Command and Status Registers)
第 675 页的图 15-13 展示了命令寄存器及错误相关字段的位置。这些位在 PCI 兼容软件的控制下被置位,以启用基线错误报告。表 15-3 定义了每个位的具体作用。
图 15-13:配置头中的命令寄存器
表 15-3:命令寄存器中的错误相关字段
| 名称 | 描述 |
|---|---|
| SERR# 启用 | 设置此位可启用向根复合体发送 ERR_FATAL 和 ERR_NONFATAL 错误消息。这些错误大致相当于 PCI 中断言系统错误(SERR#)信号。对于类型 1 报头(桥接器),此位控制从次级接口向主级接口转发 ERR_FATAL 和 ERR_NONFATAL 错误消息。此字段对 ERR_COR 消息无影响。 |
表 15-3:命令寄存器中的错误相关字段(续)
| 名称 | 描述 |
|---|---|
| 奇偶校验错误响应 | 设置该位将启用对状态寄存器中主数据奇偶校验错误位的投毒 TLP 记录。毒化数据包表示数据异常,大致相当于 PCI 奇偶校验错误。 |
第 676 页的图 15-14 展示了配置状态寄存器及错误相关位域的位置。第 677 页的表 15-4 定义了各位置位的触发条件,以及启用错误报告时设备所采取的操作。
图 15-14:配置头中的状态寄存器
表 15-4:状态寄存器中的错误相关字段
| 错误相关位 | 描述 |
|---|---|
| 检测到奇偶校验错误 | 由接收到损坏 TLP 的端口设置。无论奇偶校验错误响应位的状态如何,该状态位都会更新。 |
| 信号系统错误 | 由已报告不可纠正错误(ERR_FATAL 或 ERR_NONFATAL)且命令寄存器中 SERR# 使能位已设置的端口所设置。 |
| 收到主设备中止 | 由收到状态为 UR(不支持请求)的完成包的请求方设置。这被视为类似于 PCI 主设备中止,因为目标未“认领该事务”。 |
| 收到目标中止 | 由请求者设置,该请求者收到状态为 CA(完成者中止)的完成消息。这类似于 PCI 目标中止,表示目标出现了编程违规或内部错误状况。 |
| 发出目标中止 | 由处理请求(无论是发布还是非发布)的完成者设置,作为完成者中止。如果是非发布请求,则会发送一条完成状态为 CA 的完成消息。 |
| 主数据奇偶校验错误 | 对于类型 0 报头(例如端点),如果命令寄存器中的奇偶校验错误响应位已设置,并且它要么发起了一个中毒请求,要么接收了一个中毒完成,则此位将被置位。对于类型 1 报头(例如交换机和根端口),如果命令寄存器中的奇偶校验错误响应位已设置,并且它要么向上游发起了一个中毒请求,要么向下游接收了一个中毒完成,则此位将被置位。 |
15.9.2 基线错误处理(Baseline Error Handling)
基线能力要求使用 PCI Express 能力结构。这些寄存器包含错误检测和处理字段,与仅使用 PCI 兼容的错误处理相比,它们能够以更精细的粒度描述错误的性质以及是否报告该错误。
第 678 页的图 15-15 展示了 PCI Express 能力结构。其中部分寄存器支持以下功能:
- 启用/禁用错误报告(错误消息生成)
- 提供错误状态
- 提供链路训练状态并启动链路重新训练
图 15-15:PCI Express 能力结构
15.9.2.1 启用/禁用错误报告(Enabling/Disabling Error Reporting)
设备控制寄存器允许软件为四种错误事件启用三种不同错误消息的生成,而设备状态寄存器则允许其查看已检测到哪种错误。这四种错误情况包括:
- 可纠正错误
- 非致命错误
- 致命错误
- 不支持的请求错误
请注意,这里唯一明确识别出的错误是“不支持的请求”。虽然从技术上讲,“不支持的请求”属于非致命错误的一个子集,并且在报告时甚至通过 ERR_NONFATAL 消息来指示,但它拥有自己独立的启用位和状态位。这是因为在系统枚举过程中,“不支持的请求”是必然会发生的(每当尝试读取系统中实际不存在的功能的配置空间时),但它们绝不能作为错误被报告。枚举软件可能具有非常有限的错误处理能力,如果要求它停下来处理错误,它可能会失败。因此,软件在枚举期间不希望为 UR 情况生成错误消息,但确实希望了解可能检测到的任何其他非致命错误。(有关枚举期间“不支持的请求”的更多详细信息,请参见第 105 页的“发现功能是否存在”一节。)
第 679 页的表 15-5 列出了每种错误类型及其相关的错误分类。
表 15-5:错误的默认分类
| 分类与严重性 | 错误名称 | 检测到的层 |
|---|---|---|
| 可纠正 | 接收器错误 | 物理 |
| 可纠正 | 坏 TLP | 链路 |
| 可纠正 | 坏 DLLP | 链路 |
| 可纠正 | 重放次数回绕 | 链路 |
| 可纠正 | 重放定时器超时 | 链路 |
| 可纠正 | 建议性非致命错误 | 事务 |
| 可纠正 | 已纠正的内部错误 | 内部 |
| 可纠正 | 报头日志溢出 | 事务 |
| 不可纠正 - 非致命 | 收到投毒 TLP | 事务 |
| 不可纠正 - 非致命 | ECRC 校验失败 | 事务 |
表 15-5:错误的默认分类(续)
| 分类与严重程度 | 错误名称 | 检测层 |
|---|---|---|
| 不可纠正 - 非致命 | 不支持的请求 | 事务 |
| 不可纠正 - 非致命 | 完成超时 | 事务 |
| 不可纠正 - 非致命 | 完成者中止 | 事务 |
| 不可纠正 - 非致命 | 意外完成 | 事务 |
| 不可纠正 - 非致命 | ACS 违规 | 事务 |
| 不可纠正 - 非致命 | MC 阻塞的 TLP | 事务 |
| 不可纠正 - 非致命 | 原子操作出口被阻止 | 事务 |
| 不可纠正 - 非致命 | TLP 前缀被阻止 | 事务 |
| 不可纠正 - 致命 | 不可纠正的内部错误(可选) | 内部 |
| 不可纠正 - 致命 | 意外断开(可选) | 链路 |
| 不可纠正 - 致命 | 接收器溢出(可选) | 事务 |
| 不可纠正 - 致命 | DLL 协议错误 | 链路 |
| 不可纠正 - 致命 | 接收器溢出 | 事务 |
| 不可纠正 - 致命 | 流控制协议错误 | 事务 |
| 不可纠正 - 致命 | 格式错误的 TLP | 事务 |
设备控制寄存器(Device Control Register)
设置设备控制寄存器(如图 15-16 第 681 页所示)中的位,可启用发送相应错误消息以报告错误。不支持的请求错误被指定为非致命错误,并通过非致命错误消息报告,但仅在 UR 报告启用位被设置时生效。
为了使功能实际发送错误消息,需要在设备控制寄存器中设置相应的使能位,或者对于致命错误和非致命错误,应设置 SERR# 使能。对于不可纠正错误,如果命令寄存器中的 SERR# 使能位已设置,或者设备控制寄存器中的相应使能位已设置,则会发送相应的错误消息(ERR_FATAL 或 ERR_NONFATAL)。
对于可纠正错误,只有当设备控制寄存器中的可纠正错误报告使能位被设置时,功能才会发送 ERR_COR 消息。PCI 兼容机制中没有控制 ERR_COR 消息的选项,这很合理,因为在 PCI 中不存在可纠正错误的概念。
图 15-16:与错误处理相关的设备控制寄存器字段
设备状态寄存器(Device Status Register)
如图 15-17(第 682 页)所示,每当检测到与其分类相关的错误时,设备状态寄存器中的错误状态位就会被置位,无论设备控制寄存器中的错误报告使能位如何设置。由于不支持请求错误被视为非致命错误,当这些错误发生时,非致命错误检测状态位和不支持请求检测状态位都会被置位。与其他几个状态位类似,这些位具有“粘性”(其值不会因复位事件而清除,因此即使需要复位才能使链路正常工作以读取状态,它们仍可用于诊断问题)。
图 15-17:与错误处理相关的设备状态寄存器位字段
15.9.3 根节点对错误消息的响应(Root’s Response to Error Message)
当根复合体接收到错误消息时,其采取的操作部分取决于根控制寄存器中的设置。图 15-18 展示了该寄存器,并突出显示了三个字段,这些字段指定接收到的错误消息是否应报告为系统错误。在某些基于 x86 的系统中,如果错误被启用以触发系统错误,则很可能会发出 NMI(不可屏蔽中断)信号。
报告错误消息的其他选项无法通过标准寄存器进行配置。最可能的情况是,会向处理器发出中断信号,调用错误处理程序,该程序可能会记录错误并尝试清除问题。
图 15-18:根控制寄存器
15.9.4 链路错误(Link Errors)
链路故障通常在物理层检测到,并通知给数据链路层。对于下游设备而言,如果链路发生了致命错误且无法正常运行,则无法向主机报告该错误。在这种情况下,必须由上游设备报告错误。如果软件能够将错误定位到特定链路,处理不可纠正错误(或防止未来出现不可纠正错误)的一个步骤是重新训练链路。链路控制寄存器包含一个位,允许软件强制链路重新训练,如第 684 页图 15-19 所示。如果这能解决问题,操作将在极短的停机时间后恢复。
图 15-19:链路控制寄存器 - 强制链路重新训练
一旦请求重新训练,软件可以轮询链路状态寄存器中的链路训练位,以查看训练是否已完成。图 15-20 突出显示了该状态位。当该位为 1b 时,链路仍处于重新训练过程中(或尚未开始重新训练)。一旦物理层报告链路处于活动状态(即训练过程已成功完成),硬件将清除该位。
图 15-20:链路状态寄存器中的链路训练状态
15.10 高级错误报告(AER)(Advanced Error Reporting)
第 686 页图 15-21 所示的高级错误报告结构支持更为复杂的错误处理机制。这些寄存器提供了以下多项附加功能:
- 日志记录中更精细地划分实际发生的错误类型
- 控制每种不可纠正错误类型的严重程度
- 支持记录发生错误的数据包头部信息
- 标准化根节点通过中断报告接收到的错误消息的控制方式
- 识别 PCIe 拓扑结构中错误的来源
- 能够屏蔽报告单个类型的错误
图 15-21:高级错误能力结构
| 偏移 | 寄存器/字段 |
|---|---|
| 00h | PCIe 扩展能力寄存器 |
| 04h | 不可纠正错误状态寄存器 |
| 08h | 不可纠正错误掩码寄存器 |
| 0Ch | 不可纠正错误严重性寄存器 |
| 10h | 可纠正错误状态寄存器 |
| 14h | 可纠正错误掩码寄存器 |
| 18h | 高级错误能力与控制寄存器 |
| 1Ch | 报头日志寄存器 |
| 2Ch | 根错误命令寄存器 |
| 30h | 根错误状态寄存器 |
| 34h | 错误源 ID 寄存器 |
| 38h | TLP 前缀日志寄存器(支持 TLP 前缀的功能) |
15.10.1 高级错误能力与控制(Advanced Error Capability and Control)
让我们从查看高级错误能力与控制寄存器开始讨论 AER。端到端 CRC(ECRC)的生成与校验需要 AER 支持,该寄存器(如图 15-22 第 687 页所示)会报告该设备是否支持此功能。若支持,配置软件可通过设置相应位来启用(并强制)其使用。
该寄存器的低 5 位包含首次错误指针,当不可纠正错误状态位更新时由硬件设置。共有 32 个状态位,首次错误指针指示哪个未被屏蔽的不可纠正错误最先被检测到,即当其他所有状态位仍为 0 时被置位的状态位。首次错误最为关键,因为后续错误可能由首次错误引发。
图 15-22:高级错误能力与控制寄存器
从 2.1 规范修订版开始,该能力得到增强,可支持追踪多个错误。因此,若多个错误状态位被设置后又清除,该指针的实际含义更接近于“最旧错误指针”。当软件清除对应状态位时,硬件会更新该指针,此时指针将指向下一个被检测到的错误(不可纠正错误列表参见第 691 页图 15-25)。有趣的是,若同一错误被多次检测到,则下一个错误可能仍是该错误,导致更新后的指针仍指向相同值。
由于不可纠正状态寄存器中可以记录多个错误,因此同时存储多个报头将非常有帮助。硬件必须设计为至少记录一个报头,但允许支持更多报头。如果支持,则多报头记录能力位将被置位,并且可以使用多报头记录使能位来启用存储多个报头。每当首个错误指针指向一个未置位或未实现的状态位位置时,表示没有更多不可纠正错误需要处理。
该寄存器中的最后一位——TLP 前缀日志存在位,用于指示 TLP 前缀日志寄存器是否包含首个错误指针所指示的不可纠正错误的有效信息。
该寄存器及其他 AER 寄存器中的字段具有多种特性,缩写如下:
- RO - 只读,由硬件设置
- ROS - 只读且粘滞(参见下一节关于粘滞位的说明)
- RsvdP - 保留且保持。这些位不得用于任何目的,但软件必须谨慎维护其包含的任何值。
- RsvdZ - 保留且清零。不得用于任何目的且必须始终写入零值的位。
- RWS - 可读、可写且粘滞
- RW1CS - 可读、写 1 清零且粘滞
15.10.2 处理粘滞位(Handling Sticky Bits)
多个 AER 寄存器字段采用了粘滞位,这意味着复位操作不会清除其内容。所有其他寄存器字段在复位时会被强制恢复为默认值,但这些字段不会。这是一个很好的设计,因为链路可能会遇到无法通过复位清除的故障。如果问题出在故障链路的下游设备上,其寄存器内容在链路恢复正常之前无法访问,而复位操作可以恢复链路。但如果寄存器在复位时被清除,信息就会丢失。为了解决这个问题,粘滞位能够在复位过程中保留错误状态信息。具体来说,粘滞位可以承受 FLR(功能级复位)、热复位和暖复位,因为电源仍可维持其活动状态。如果存在辅助电源(如 Vaux)在主电源关闭时仍保持其有效,它们甚至可能承受冷复位。
15.10.3 高级可纠正错误处理(Advanced Correctable Error Handling)
高级错误报告功能能够记录检测到的具体可纠正错误。这些错误可用于向主机系统发起可纠正错误消息。尽管系统操作仍会正常进行,但报告可纠正错误非常有用,因为它能让系统软件了解哪些组件出现故障,并预测这些组件未来是否可能完全失效。
15.10.3.1 高级可纠正错误状态(Advanced Correctable Error Status)
无论是否通过错误消息报告错误,可纠正错误都会自动设置高级可纠正错误状态寄存器(见图 15-23,第 689 页)中的相应位。这些位由软件通过向该位写入“1”来清除,因此被标记为 RW1CS。
图 15-23:高级可纠正错误状态寄存器
接收器错误(可选)——物理层检测到传入数据包中存在错误。该数据包在物理层被丢弃,为其分配的任何缓冲区空间被释放,并通知链路层发生了接收错误。
坏 TLP——数据链路层检测到数据包存在 LCRC 错误、序列号失序或被置空(nullified)的数据包。在每种情况下,链路层都会丢弃该数据包,并向发送方报告 Nak DLLP,从而触发 TLP 重传。
坏 DLLP——数据链路层发现传入的 DLLP 存在 16 位 CRC 校验失败,因此该数据包被丢弃。后续相同类型的 DLLP 预计将弥补其所包含的信息。
REPLAY_NUM 翻转 - 在数据链路层,一组 TLP 连续四次发送失败(未收到确认),且此计数器已翻转回零。硬件将自动重新训练链路以尝试清除故障状态,然后通过重放重放缓冲区中的内容重新开始该序列。
重放定时器超时 - 在数据链路层,已传输的 TLP 在超时周期内未收到确认(Ack 或 Nak)。硬件自动重放所有未确认的 TLP,即重放缓冲区中的所有数据包。
建议性非致命错误 —— 检测到这些情况(参见第 670 页的“建议性非致命错误”)时,会在相应的不可纠正错误状态寄存器中记录,并在此处作为可纠正错误记录。如果启用,还可能生成可纠正错误消息。
已纠正内部错误(可选) - 检测到设备内部错误,但已纠正或规避,未导致异常行为。
报头日志溢出(可选)- 报头日志中可存储的最大报头数量已达上限。若高级错误能力与控制寄存器中的“多报头记录使能位”未置位,则该数量仅为 1。
15.10.3.2 高级可纠正错误屏蔽(Advanced Correctable Error Masking)
可纠正错误的报告由设备控制寄存器中的“可纠正错误使能位”统一控制,同时也可通过图 15-24 所示的可纠正错误屏蔽寄存器进行单独控制。屏蔽位的默认状态为清零,这意味着当检测到任何已使能的可纠正错误时(即“可纠正错误使能位”已置位),可发送 ERR_COR 消息。但软件可选择设置该屏蔽寄存器中的对应位,以在检测到特定错误时阻止消息发送。
图 15-24:高级可纠正错误屏蔽寄存器
15.10.4 高级不可纠正错误处理(Advanced Uncorrectable Error Handling)
对于不可纠正的错误,AER 提供了追踪具体错误类型的能力,可控制将其视为致命错误或非致命错误,并选择是否向根节点发送不可纠正错误消息。
15.10.4.1 高级不可纠正错误状态(Advanced Uncorrectable Error Status)
当发生不可纠正的错误时,无论该错误是否会被报告给根节点,硬件都会自动设置此寄存器中的相应位(参见第 691 页图 15-25)。如果发生多个错误,硬件将为每个错误设置相应的位,并在高级错误能力与控制寄存器的首次错误指针字段中记录哪个错误最先发生。甚至可能在第一个错误被处理之前,就检测到同一错误的多个实例。符合 2.1 或更高版本规范的硬件将能够跟踪特定设计数量的此类情况。
图 15-25:高级不可纠正错误状态寄存器
以下列表从右至左描述了每个寄存器位:
- 未定义 - 此前,该第一位表示物理层的链路训练失败,但该含义已在 1.1 修订版中被移除。
规范要求:软件现在必须忽略该位中的任何值,但可以写入任意值。此信息不再需要,因为第 5 位(意外断开错误)已通过更广泛的含义包含了相同的信息:链路在物理层未进行通信。
数据链路协议错误 - 由数据链路层协议错误引起,包括确认/否定确认(Ack/Nak)重试机制。例如,发送方接收到序列号与未确认的 TLP 或 ACKD_SEQ 编号不对应的 Ack 或 Nak。
意外断开 - 如果物理层意外报告 LinkUp 被清除(链路不再通信),除非属于允许的例外情况,否则将被视为错误。例如,如果链路禁用位已被设置,则 LinkUp 被清除是预期行为,此情况不构成错误。该位仅对下游端口有效,这合乎逻辑,因为如果链路无法工作,将无法从上游端口读取状态。
投毒 TLP - 检测到 EP 位被设置的 TLP。
流量控制协议错误(可选)——与流量控制机制失败相关的错误。例如:接收方报告的数据信用额度超过 2047。
完成超时——在发送非发布请求后,未在规定时间内收到完成响应。
完成者中止(可选)——由于请求本身存在问题或完成者自身故障,完成者无法满足请求。
意外完成——请求方收到一个完成响应,但该响应与任何等待完成的请求都不匹配。
接收器溢出(可选)- 到达的 TLP 数量超过了接收缓冲区的容量,导致溢出错误。
格式错误的 TLP - 由接收到的 TLP 头部相关错误引起(参见第 666 页的“格式错误的 TLP”)。
ECRC 错误(可选)- 由接收端的 ECRC 校验失败引起。
不支持的请求错误 - 完成者不支持该请求。请求格式正确且无其他错误,但完成者无法满足该请求,可能是因为该命令对此设备无效。
ACS 违规 - 在接收到的发布或非发布请求中检测到访问控制错误。
不可纠正的内部错误 - 设备中检测到的内部错误无法由硬件本身纠正或规避。
MC 阻塞 TLP - 指定用于多播路由的 TLP 被阻塞。例如,出口端口可被编程为阻塞任何带有未转换地址到达的 MC 命中(参见第 896 页的“路由多播 TLP”)。
原子操作出口阻塞 - 路由元件的出口端口可被编程为阻止 AtomicOps 转发给不应看到它们的代理(参见第 897 页的“AtomicOps”)。
TLP 前缀阻塞错误 - 路由元件的出口端口可编程设置为不转发包含端到端 TLP 前缀的 TLP。如果检测到此类 TLP,将丢弃该 TLP 并报告此错误。更多信息请参见第 899 页的“TPH(TLP 处理提示)”。
回顾一下,能力与控制寄存器中的首个错误指针指示自上次更新以来第一个到达的未屏蔽不可纠正错误。错误处理软件可读取该指针以确定应优先调查哪个错误。例如,若指针值为 18d,则表示不可纠正状态寄存器中的第 18 位是首个错误,即格式错误的 TLP。处理完该错误后,软件向状态寄存器的第 18 位写入 1 以清除该事件,从而将首个错误指针更新为次近的错误。
15.10.4.2 选择不可纠正错误的严重性(Selecting Uncorrectable Error Severity)
软件可以通过此寄存器选择是否将不可纠正错误视为致命错误,从而允许针对不同应用场景对错误进行差异化处理。例如,投毒 TLP 默认属于非致命条件,在某些情况下会被视为建议性非致命错误(如前文所述)。但软件可通过将其严重性位设置为 1 将其升级为致命错误,此时该错误将不再属于建议性情况。默认严重性值如图 15-26(第 694 页)的各个位域所示(1b 表示致命,0b 表示非致命)。若这些错误已启用且未被屏蔽,被选为非致命的错误将向根复合体发送 ERR_NONFATAL 消息,而被选为致命的错误则会发送 ERR_FATAL 消息。
图 15-26:高级不可纠正错误严重性寄存器
15.10.4.3 不可纠正错误屏蔽(Uncorrectable Error Masking)
软件可以通过使用高级不可纠正错误掩码寄存器(如图 15-27 所示,第 694 页)来屏蔽个别错误,从而避免发送错误消息。默认情况下,每种错误类型均允许发送错误消息(所有掩码位均被清除)。
图 15-27:高级不可纠正错误掩码寄存器
15.10.5 报头日志记录(Header Logging)
头部日志记录仅用于 TL 层,具有 4DW、16 字节容量,用于记录首个错误指针。
高级错误报告结构中的 4DW 部分用于存储接收到的 TLP 的头部,该 TLP 发生了未屏蔽的不可纠正错误。由于头部日志仅在接收到的 TLP 存在物理层或数据链路层未发现的问题时才有用,因此可能的情况数量有限,如表 15-6(第 695 页)所示。如前所述,当实现可选的 AER 功能时,硬件需要能够记录至少一个头部,尽管它可能支持记录更多头部。
当首个错误指针有效时,如果错误是由传入的 TLP 引起的,则头部日志包含相应错误的头部。更新不可纠正错误状态寄存器将导致头部日志寄存器也按顺序更新为下一个值,即下一个检测到的不可纠正错误。由于硬件只能跟踪有限数量的头部,因此软件必须足够快地处理不可纠正错误,以避免
头部日志溢出发生 当支持的日志寄存器数量超出限制,或未设置多报头日志启用位时,报头空间将耗尽。若报头日志容量达到上限,这本身属于可纠正错误(报头日志溢出)。当检测到新的不可纠正错误时,若支持的日志寄存器数量已超限,或未设置多报头日志启用位且首个错误指针已有效,则可能发生此情况。
表 15-6:可使用报头日志寄存器的错误类型
| 错误名称 | 默认分类 |
|---|---|
| 收到中毒的 TLP | 不可纠正-非致命 |
| ECRC 校验失败 | 不可纠正-非致命 |
| 不支持的请求 | 不可纠正-非致命 |
| 完成者中止 | 不可纠正-非致命 |
| 意外完成 | 不可纠正 - 非致命 |
| ACS 违规 | 不可纠正 - 非致命 |
| 格式错误的 TLP | 不可纠正 - 致命错误 |
15.10.6 根复合体错误跟踪与报告(Root Complex Error Tracking and Reporting)
根复合体是 PCIe 拓扑结构中所有设备错误消息的目标。根复合体接收到的错误会更新状态寄存器,并在启用的情况下向主机系统报告。
15.10.6.1 根复合体错误状态寄存器(Root Complex Error Status Registers)
当根复合体接收到错误消息时,会在根错误状态寄存器(图 15-28,第 697 页)中设置状态位。该寄存器指示所接收错误的类型,以及是否接收到同一类型的多个错误。需要注意的是,根端口自身检测到的错误也会设置这些状态位,如同该端口向自身发送了错误消息一样。这些状态位包括:
- 已接收 ERR_COR
- 已接收多个 ERR_COR - 已接收到 ERR_COR 消息,或在 ERR_COR 已接收位已置位的情况下检测到未屏蔽的根端口可纠正错误。
- 收到 ERR_FATAL/NONFATAL
- 多次收到 ERR_FATAL/NONFATAL - 已收到 ERR_FATAL 或 ERR_NONFATAL 消息,或检测到未屏蔽的根端口不可纠正错误,且 ERR_FATAL/NONFATAL 接收位已置位。
系统可能为可纠正错误、非致命错误和致命错误分别实现独立的软件错误处理程序,因此该寄存器包含用于区分不可纠正错误是致命还是非致命的位:
- 如果收到的第一条不可纠正错误消息是致命的,则“首次不可纠正致命”位会与“已收到致命错误消息”位一同置位。
- 如果接收到的首个不可纠正错误消息为非致命错误,则设置“非致命错误消息已接收”位。(若后续不可纠正错误为致命错误,则会设置“致命错误消息已接收”位,但由于“首个不可纠正致命错误”位仍保持清零状态,软件可判定首个不可纠正错误为非致命错误)。
图 15-28:根错误状态寄存器
最后,检测到上述事件时,可能已启用中断(通过根错误命令寄存器)发送至主机系统。为此,本寄存器中的 5 位中断消息编号提供待使用的 MSI 或 MSI-X 向量编号,共有 32 种可能。对于 MSI,该编号为基准数据模式的偏移量;对于 MSI-X,则代表待使用的表项条目,即使代理支持超过 32 个条目,也必须使用前 32 个之一。此只读值由硬件设置,若分配给设备的 MSI 消息数量发生变化,必须自动更新该值。
15.10.6.2 高级源 ID 寄存器(Advanced Source ID Register)
软件错误处理程序可能需要读取并清除检测到并报告错误的设备中的状态寄存器。为此,错误消息包含第一个报告该错误类型的设备的 ID(总线:设备:功能)。如果 ERR_FATAL/NONFATAL 位尚未设置(表示这是第一个错误),则源 ID 寄存器会从传入的 ERR_FATAL/NONFATAL 消息中捕获该 ID。同样,第一个接收到的 ERR_COR 消息的源 ID 也会被捕获,如图 15-29(第 698 页)所示。
图 15-29:高级源 ID 寄存器
15.10.6.3 根错误命令寄存器(Root Error Command Register)
根复合体为三种错误类别分别设置了独立的使能位,用于控制该错误类型是否会产生中断以调用错误处理程序,如图 15-30(第 698 页)所示。所产生的中断可以是 MSI 或 MSI-X,具体参见第 697 页的“根复合体错误状态寄存器”。一旦接收到中断,被调用的错误处理程序通常会首先读取根复合体状态寄存器以确定错误的性质,然后向下定位到错误的源 BDF,读取标准状态寄存器以及可能的设备特定寄存器,以确定发生了什么错误以及应如何处理。
图 15-30:高级根错误命令寄存器
15.11 错误记录与报告摘要(Summary of Error Logging and Reporting)
规范中包含第 699 页图 15-31 的流程图,展示了功能在检测到错误时所执行的操作。虚线框内的部分突出显示了当存在可选 AER 能力结构时新增的条目。
图 15-31:功能内部错误处理流程图
15.12 软件错误调查的示例流程(Example Flow of Software Error Investigation)
既然我们已经了解了 PCIe 中用于检测、记录和报告错误的所有机制,那么探讨软件如何查找并利用这些信息来确定如何处理所报告的错误将颇具价值。
本示例假设原始功能及其上游的根端口均支持 AER。若缺乏 AER 支持,用于错误记录的标准化寄存器将非常有限。
本示例所使用的系统如图 15-32(第 701 页)所示。根端口的 BDF 为 0:28:0,并已配置为在接收到 ERR_FATAL 或 ERR_NONFATAL 消息时生成中断。接下来,我们将跟随错误处理软件的步骤,以确定发生了哪些错误、错误发生的位置以及检测到这些错误的数据包。
由于根端口 0:28:0 产生中断,错误处理软件已被调用。以下步骤仅为示例,但说明了错误处理软件收集错误信息的过程。
- 软件根据使用的中断向量得知是根端口 0:28:0 调用了错误处理程序。由于使用 MSI 或 MSI-X 中断来报告错误,每个根端口将拥有自己独特的一组中断向量。
- 错误处理程序读取 0:28:0 上 AER 结构的根错误状态寄存器,以确定该根端口已接收到哪些类型的错误消息。该寄存器中的值为 0800_007Ch,表明该根端口未收到任何 ERR_COR 消息,但已收到 ERR_FATAL 和 ERR_NONFATAL 消息,且其接收到的第一个不可纠正错误消息为 ERR_FATAL。
- 下一步是确定该根端口下的哪个 BDF 发送了第一个不可纠正错误。随后,软件读取根端口的源 ID 寄存器,发现值为 0500_0000h,表明第一个不可纠正错误的源 BDF 为 5:0:0。
- 现在软件知道根端口 0:28:0 接收到的第一个不可纠正错误是源自 BDF 5:0:0 的致命错误。基于此信息,软件随后读取 BDF 5:0:0 的不可纠正错误状态寄存器,以查看该 BDF 上具体发生了哪些不可纠正错误。读取返回的值为 0004_1000h,这意味着该 BDF 至少检测到一个格式错误的 TLP 和一个投毒 TLP。但错误处理程序真正关心的是哪个错误最先发生,因为那才是应该优先处理的错误。
- 为了确定多个不可纠正错误中哪个最先发生,软件随后读取 5:0:0 的高级错误能力与控制寄存器,发现值为 0000_0012h,其中首个错误指针值为 12h,这意味着第一个不可纠正错误是格式错误的 TLP(第 18d 位),而非投毒 TLP(第 12d 位)。
图 15-32:错误调查示例系统
- 现在错误处理程序知道 5:0:0 处的第一个不可纠正错误是格式错误的 TLP,它可以检查 Header Log 寄存器来查看格式错误数据包的报头,因为这是会记录报头的错误类型之一。在读取 Header Log 寄存器时,它发现了以下四个双字:
- 6000_8080h - 第 1 个双字
- 0000_04FFh - 第 2 个双字
- FB80_1000h - 第 3 个双字
- 0000_0001h - 第 4 个双字
- 对这 4 个双字的评估将格式错误的数据包识别为:内存写入,4DW 报头,TC=0,TD=1,EP=0,Attr=0,AT=0,Length=80h(128 个双字或 512 字节),请求者 ID=0:0:0,标签=4,字节使能=FFh,地址=1_FB80_1000h。 该数据包的头部看起来全部正确,每个字段均使用有效编码,因此软件必须深入挖掘才能发现为何该数据包被视为格式错误的 TLP。在此示例中,假设进一步检查 5:0:0 的配置空间后,软件发现该功能启用的最大有效载荷大小为 256 字节,但此数据包包含 512 字节。这种情况将被目标设备(本例中为 5:0:0)视为格式错误的 TLP。
如果您想验证对此错误调查流程的理解,请继续评估在 4:0:0 上检测到的首个不可纠正错误是什么。
如果你喜欢冒险,并想在真实系统(比如你的台式机或笔记本电脑)上查看这类信息,可以下载 MindShare Arbor 软件(www.mindshare.com/arbor)。你可以在基于 x86 的机器上运行它,它会扫描你的系统并显示所有可见的 PCI 兼容设备,同时解码其配置空间以便于解读。